import spidev
import time
import RPi.GPIO as GPIO
import os


AD_OS0=23
AD_OS1=24
AD_OS2=27
AD_RESET=17
#CONVST_A CONVST_B都接的是gpio5
AD_CONVERT=5
AD_RANGE=4
AD_STANDBY=25
AD_BUSY=18


class HatError(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return str(self.value)


class ScanResult:
    def __init__(self, data, hardware_overrun=False, buffer_overrun=False, timeout=False):
        self.data = data
        self.hardware_overrun = hardware_overrun
        self.buffer_overrun = buffer_overrun
        self.timeout = timeout


class ad7606:
    def __init__(self):
        spi_gpio_list = [AD_OS0, AD_OS1, AD_OS2, AD_RESET, AD_RANGE, AD_STANDBY]
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(spi_gpio_list, GPIO.OUT, initial=GPIO.LOW)
        GPIO.setup(AD_CONVERT, GPIO.OUT, initial=GPIO.LOW)

        self.spi = spidev.SpiDev()
        self.spi.open(0, 0)
        self.spi.max_speed_hz=15600000
        self.channel_mask = 0
        self.samples_per_channel = 0
        self.scan_rate = 0
        self.options = 0
        self.read_request_size = 0
        self.timeout = 0 #秒
        self.max_rate = 19060 #最大采样率
        self.delay_us = 0

        self.take_sample = False
        self.samples = []

        GPIO.output(AD_STANDBY, GPIO.HIGH)
        GPIO.output(AD_RESET, GPIO.HIGH)
        time.sleep(0.001)
        GPIO.output(AD_RESET, GPIO.LOW)

        self.fs_val = 5
        self.ad_range_10v()  #+-5V会有削顶现象

        #self.ad7606_startConv()
        
    def ad_range_10v(self):
        GPIO.output(AD_RANGE, GPIO.HIGH)
        self.fs_val = 10

    def ad_range_5v(self):
        GPIO.output(AD_RANGE, GPIO.LOW)
        self.fs_val = 5
    
    def ad_get_data(self):
        return self.spi.readbytes(16)#6通道,16位有符号类型

    def _spi_read(self):
        bytes = self.ad_get_data()
        for ch in self.channel_mask:
            adc_val = (bytes[2*ch] << 8) | bytes[2*ch+1]
            if (adc_val & (1 << 15)) != 0:
                adc_val = adc_val - (1 << 16)
            adc_val = self.fs_val * adc_val / 32768
            self.samples.append(adc_val)

    def a_in_scan_start(self, channel_mask, samples_per_channel, scan_rate, options):
        self.channel_mask = channel_mask
        self.samples_per_channel = samples_per_channel
        self.scan_rate = scan_rate
        self.options = options
        if scan_rate > 7200:
            raise HatError("scan rate > 7200")  # rate > 7200 is too challenging for software sampling
        for ch in channel_mask:
            if ch > 7:
                raise HatError("channel > 7")
        self.samples = []

    def a_in_scan_read(self, read_request_size, timeout=10):
        enter_time_ns = time.time_ns()
        period_ns = 1000000000 / self.scan_rate
        request_period_ns = period_ns * read_request_size
        timeout_happen = False
        while not timeout_happen:
            self.samples = []
            try_begin_time = time.time_ns()
            while len(self.samples) < read_request_size * len(self.channel_mask):
                start_ns = time.time_ns()
                # check timeout
                if start_ns - enter_time_ns > timeout * 10e9:
                    timeout_happen = True
                    break
                # trigger sample
                GPIO.output(AD_CONVERT, GPIO.LOW)
                GPIO.output(AD_CONVERT, GPIO.LOW)
                GPIO.output(AD_CONVERT, GPIO.LOW)
                GPIO.output(AD_CONVERT, GPIO.HIGH)
                GPIO.output(AD_CONVERT, GPIO.HIGH)
                GPIO.output(AD_CONVERT, GPIO.HIGH)
                # read
                self._spi_read()
                # ensure rate
                while time.time_ns() < (start_ns + period_ns - 100):  # 100ns overhead for while condition check
                    pass
            # check if this request batch meets request_period_ns time length +-1%
            try_end_time = time.time_ns()
            if request_period_ns * 0.98 < (try_end_time - try_begin_time) < request_period_ns * 1.02:
                break

        return ScanResult(self.samples[0:read_request_size*len(self.channel_mask)], timeout=timeout_happen)
        
    def a_in_scan_stop(self):
        pass

    def a_in_scan_cleanup(self):
        self.samples = []


        
#adc_data = [0 for i in range(10000)]
if __name__ == '__main__':
    read_request_size = 1000
    timeout = 5
    scan_rate = 15000

    hat = ad7606()
    hat.a_in_scan_start([0, 1, 2, 3, 4, 5], None, scan_rate, 0)
    adc = hat.a_in_scan_read(read_request_size, timeout)
    hat.a_in_scan_stop()
    hat.a_in_scan_cleanup()
    print(adc.data)



